﻿using System;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

using SimpleForum.Contracts;
using SimpleForum.Data.Services;
using SimpleForum.Classes.Core.Providers;
using SimpleForum.Classes.Achievements;

namespace SimpleForum.Classes.Web
{
    public class BootStrapper : HttpApplication
    {
        [ImportMany] private IEnumerable<Lazy<IRouteRegistrar, IRouteRegistrarMetadata>> RouteRegistrars;
        [Import] private ImportControllerFactory ControllerFactory;
        private static IEnumerable<IPluginInfo> ActionVerbs;

        public static Composer Composer { get; private set; }

        protected void Application_Start()
        {
            PreCompose();
            Composer = CreateComposer();
            Compose();
            ControllerBuilder.Current.SetControllerFactory(ControllerFactory);
            ViewEngines.Engines.Add(new SimpleViewEngine());

            Initialise();
            RegisterRoutes();
        }

        protected virtual void Initialise()
        {
            DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
            ConfigurationService.UpdateCache();

            if (ConfigurationService.AchievementsEnabled)
            {
                // fix me, I don't like explictly creating new achievements here. we should use reflection or something and find out what inherits AchievementWorker
                new NewbieAchievement();
                new CommentatorAchievement();
                new EditorAchievement();
                new ComedianAchievement();
                new NeighbourhoodWatchAchievement();
            }
        }
        
        protected void Application_AuthenticateRequest(object Sender, EventArgs e)
        {
            HttpCookie Cookie = Request.Cookies[FormsAuthentication.FormsCookieName];

            if (Cookie != null)
            {
                FormsAuthenticationTicket Ticket = FormsAuthentication.Decrypt(Cookie.Value);
                PrincipalProvider Principal = new PrincipalProvider(new IdentityProvider(Ticket.Name, "Forms"));

                HttpContext.Current.User = Principal;
                System.Threading.Thread.CurrentPrincipal = Principal;
            }
        }

        protected virtual Composer CreateComposer()
        {
            var composer = new Composer();

            GetDirectoryCatalogs()
                .ForEach(composer.AddCatalog);

            composer.AddExportProvider(
                new DynamicInstantiationExportProvider(),
                (provider, container) => ((DynamicInstantiationExportProvider)provider).SourceProvider = container);

            return composer;
        }

        private List<DirectoryCatalog> GetDirectoryCatalogs()
        {
            var list = new List<DirectoryCatalog>();
            string applicationPath = MapPath("~/");

            foreach(var catalog in GetDirectoryCatalogs(MapPath("~/bin")))
            {
                list.Add(catalog);
            }
            
            foreach(var catalog in GetDirectoryCatalogs(MapPath("~/Plugins")))
            {
                list.Add(catalog);
                RegisterPath(catalog.FullPath);
            }

            var config = CompositionConfigurationSection.GetInstance();
            if (config != null && config.Catalogs != null)
            {
                foreach (var c in config.Catalogs.Cast<CatalogConfigurationElement>())
                {
                    if (!string.IsNullOrEmpty(c.Path))
                    {
                        string path = c.Path;
                        if (path.StartsWith("~") || path.StartsWith("/"))
                            path = MapPath(path);

                        foreach(var catalog in GetDirectoryCatalogs(path))
                        {
                            list.Add(catalog);
                            RegisterPath(catalog.FullPath);
                        }
                    }
                }
            }

            return list;
        }

        private void RegisterPath(string path)
        {
            AppDomain.CurrentDomain.AppendPrivatePath(path);
        }

        protected virtual void PreCompose() { }

        protected virtual void Compose()
        {
            if (Composer == null)
                return;

            Composer.Compose(this);

            ActionVerbs = Composer.ResolveAll<IPluginInfo>();
        }

        protected virtual void RegisterRoutes()
        {
            if (RouteRegistrars == null || RouteRegistrars.Count() == 0)
                return;

            var routes = RouteTable.Routes;

            var registrars = RouteRegistrars
                .OrderBy(lazy => lazy.Metadata.Order)
                .Select(lazy => lazy.Value);

            foreach (var r in registrars)
            {
                r.RegisterIgnoreRoutes(routes);
                r.RegisterRoutes(routes);
            }
        }

        protected string MapPath(string virtualPath)
        {
            return HttpContext.Current.Server.MapPath(virtualPath);
        }

        protected IEnumerable<DirectoryCatalog> GetDirectoryCatalogs(string path)
        {
            List<DirectoryCatalog> list = new List<DirectoryCatalog>();
            list.Add(new DirectoryCatalog(path));

            list.AddRange(Directory.GetDirectories(path).Select(directory => new DirectoryCatalog(directory)));

            return list;
        }

        public static IEnumerable<IPluginInfo> GetPluginsForCategory(string category)
        {
            return ActionVerbs
                .Where(l => l.Category.Equals(category, StringComparison.InvariantCultureIgnoreCase))
                .Select(l => l);
        }
    }
}